"""
股票数据 API 客户端
支持多数据源自动降级
"""
import requests
import logging
from datetime import datetime, timedelta
from typing import List, Dict, Optional
from config import config

logger = logging.getLogger(__name__)

class StockAPIClient:
    """股票 API 客户端 - 支持多数据源"""
    
    @staticmethod
    def normalize_symbol(symbol: str) -> str:
        """
        标准化股票代码
        600519 -> sh600519
        000858 -> sz000858
        """
        if symbol.startswith("sh") or symbol.startswith("sz"):
            return symbol
        if symbol.startswith("6"):
            return f"sh{symbol}"
        elif symbol.startswith("0") or symbol.startswith("3"):
            return f"sz{symbol}"
        return symbol
    
    @staticmethod
    def format_symbol(symbol: str) -> str:
        """
        格式化股票代码
        sh600519 -> 600519
        """
        return symbol.replace("sh", "").replace("sz", "")
    
    def get_realtime_data(self, symbols: List[str]) -> List[Dict]:
        """
        获取实时股票数据 - 多数据源降级
        """
        # 尝试顺序：新浪 -> 腾讯 -> 东方财富
        sources = [
            ("新浪财经", self._get_realtime_from_sina),
            ("腾讯财经", self._get_realtime_from_tencent),
            ("东方财富", self._get_realtime_from_eastmoney)
        ]
        
        for source_name, fetch_func in sources:
            try:
                logger.info(f"📡 尝试从 {source_name} 获取实时数据...")
                data = fetch_func(symbols)
                if data:
                    logger.info(f"✅ 成功从 {source_name} 获取 {len(data)} 只股票数据")
                    return data
                else:
                    logger.warning(f"⚠️ {source_name} 返回空数据")
            except Exception as e:
                logger.error(f"❌ {source_name} 失败: {e}")
                continue
        
        logger.error("❌ 所有实时数据源均不可用")
        return []
    
    def _get_realtime_from_sina(self, symbols: List[str]) -> List[Dict]:
        """从新浪财经获取实时数据"""
        normalized_symbols = [self.normalize_symbol(s) for s in symbols]
        symbols_str = ",".join(normalized_symbols)
        
        url = f"https://hq.sinajs.cn/list={symbols_str}"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://finance.sina.com.cn"
        }
        
        response = requests.get(url, headers=headers, timeout=10)
        response.encoding = 'gbk'
        
        if response.status_code != 200:
            raise Exception(f"HTTP {response.status_code}")
        
        return self._parse_sina_realtime(response.text)
    
    def _get_realtime_from_tencent(self, symbols: List[str]) -> List[Dict]:
        """从腾讯财经获取实时数据"""
        normalized_symbols = [self.normalize_symbol(s) for s in symbols]
        symbols_str = ",".join(normalized_symbols)
        
        url = f"https://qt.gtimg.cn/q={symbols_str}"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://gu.qq.com"
        }
        
        response = requests.get(url, headers=headers, timeout=10)
        response.encoding = 'gbk'
        
        if response.status_code != 200:
            raise Exception(f"HTTP {response.status_code}")
        
        return self._parse_tencent_realtime(response.text)
    
    def _get_realtime_from_eastmoney(self, symbols: List[str]) -> List[Dict]:
        """从东方财富获取实时数据"""
        stocks = []
        
        for symbol in symbols:
            try:
                normalized = self.normalize_symbol(symbol)
                market = "1" if normalized.startswith("sh") else "0"
                code = normalized[2:]
                
                url = f"https://push2.eastmoney.com/api/qt/stock/get?secid={market}.{code}&fields=f58,f107,f57,f43,f169,f170,f46,f44,f51,f52,f47,f48,f60,f45"
                headers = {
                    "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
                    "Referer": "https://quote.eastmoney.com"
                }
                
                response = requests.get(url, headers=headers, timeout=10)
                
                if response.status_code == 200:
                    data = response.json()
                    if data.get("data"):
                        stock_data = self._parse_eastmoney_data(data["data"], symbol)
                        if stock_data:
                            stocks.append(stock_data)
            except Exception as e:
                logger.error(f"东方财富解析 {symbol} 失败: {e}")
                continue
        
        return stocks
    
    def _parse_sina_realtime(self, response: str) -> List[Dict]:
        """解析新浪财经实时数据"""
        stocks = []
        lines = response.strip().split('\n')
        
        for line in lines:
            if not line:
                continue
            
            try:
                import re
                symbol_match = re.search(r'hq_str_(\w+)=', line)
                if not symbol_match:
                    continue
                
                symbol = symbol_match.group(1)
                data_start = line.find('"') + 1
                data_end = line.rfind('"')
                data = line[data_start:data_end].split(',')
                
                if len(data) < 32:
                    continue
                
                name = data[0]
                current_price = float(data[3])
                yesterday_close = float(data[2])
                change = current_price - yesterday_close
                change_percent = (change / yesterday_close * 100) if yesterday_close > 0 else 0
                volume = int(data[8])
                
                stocks.append({
                    "symbol": self.format_symbol(symbol),
                    "name": name,
                    "current_price": current_price,
                    "change": round(change, 2),
                    "change_percent": round(change_percent, 2),
                    "volume": volume,
                    "sector": None
                })
                
            except Exception as e:
                logger.error(f"解析新浪数据失败: {e}")
                continue
        
        return stocks
    
    def _parse_tencent_realtime(self, response: str) -> List[Dict]:
        """解析腾讯财经实时数据"""
        stocks = []
        lines = response.strip().split('\n')
        
        for line in lines:
            if not line or '~' not in line:
                continue
            
            try:
                # 提取数据部分
                data_start = line.find('"') + 1
                data_end = line.rfind('"')
                data = line[data_start:data_end].split('~')
                
                if len(data) < 50:
                    continue
                
                symbol = data[2]
                name = data[1]
                current_price = float(data[3])
                yesterday_close = float(data[4])
                change = current_price - yesterday_close
                change_percent = (change / yesterday_close * 100) if yesterday_close > 0 else 0
                volume = int(float(data[6])) if data[6] else 0
                
                stocks.append({
                    "symbol": symbol,
                    "name": name,
                    "current_price": current_price,
                    "change": round(change, 2),
                    "change_percent": round(change_percent, 2),
                    "volume": volume,
                    "sector": None
                })
                
            except Exception as e:
                logger.error(f"解析腾讯数据失败: {e}")
                continue
        
        return stocks
    
    def _parse_eastmoney_data(self, data: dict, symbol: str) -> Optional[Dict]:
        """解析东方财富数据"""
        try:
            name = data.get("f58", "")
            current_price = data.get("f43", 0) / 100
            change = data.get("f169", 0) / 100
            change_percent = data.get("f170", 0) / 100
            volume = data.get("f47", 0)
            
            return {
                "symbol": symbol,
                "name": name,
                "current_price": current_price,
                "change": round(change, 2),
                "change_percent": round(change_percent, 2),
                "volume": volume,
                "sector": None
            }
        except Exception as e:
            logger.error(f"解析东方财富数据失败: {e}")
            return None
    
    def get_history_data(self, symbol: str, days: int = 90) -> List[Dict]:
        """
        获取历史K线数据 - 多数据源降级
        """
        # 尝试顺序：腾讯 -> 网易 -> 东方财富（腾讯最稳定）
        sources = [
            ("腾讯财经", self._get_history_from_tencent),
            ("网易财经", self._get_history_from_netease),
            ("东方财富", self._get_history_from_eastmoney)
        ]
        
        for source_name, fetch_func in sources:
            try:
                logger.info(f"📊 尝试从 {source_name} 获取历史数据...")
                data = fetch_func(symbol, days)
                if data and len(data) > 0:
                    logger.info(f"✅ 成功从 {source_name} 获取 {len(data)} 条历史数据")
                    return data
                else:
                    logger.warning(f"⚠️ {source_name} 返回空数据")
            except Exception as e:
                logger.error(f"❌ {source_name} 失败: {e}")
                continue
        
        logger.error("❌ 所有历史数据源均不可用")
        return []
    
    def _get_history_from_netease(self, symbol: str, days: int) -> List[Dict]:
        """从网易财经获取历史数据"""
        normalized_symbol = self.normalize_symbol(symbol)
        code = normalized_symbol[2:]
        market = "0" if normalized_symbol.startswith("sh") else "1"
        
        # 动态计算日期范围：从今天往前推 days 天
        end_date = datetime.now()
        start_date = end_date - timedelta(days=days + 30)  # 多取30天以应对周末和节假日
        
        logger.info(f"📅 请求网易数据: {start_date.strftime('%Y-%m-%d')} 到 {end_date.strftime('%Y-%m-%d')}")
        
        url = (
            f"https://quotes.money.163.com/service/chddata.html"
            f"?code={market}{code}"
            f"&start={start_date.strftime('%Y%m%d')}"
            f"&end={end_date.strftime('%Y%m%d')}"
            f"&fields=TCLOSE;HIGH;LOW;TOPEN;VOTURNOVER"
        )
        
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://quotes.money.163.com"
        }
        
        response = requests.get(url, headers=headers, timeout=15)
        response.encoding = 'gbk'
        
        if response.status_code != 200:
            raise Exception(f"HTTP {response.status_code}")
        
        return self._parse_netease_csv(response.text)
    
    def _get_history_from_tencent(self, symbol: str, days: int) -> List[Dict]:
        """从腾讯财经获取历史数据"""
        normalized = self.normalize_symbol(symbol)
        
        url = f"https://web.ifzq.gtimg.cn/appstock/app/fqkline/get?param={normalized},day,,,{days},qfq"
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://gu.qq.com"
        }
        
        # 增加超时时间和重试
        max_retries = 2
        for retry in range(max_retries):
            try:
                response = requests.get(url, headers=headers, timeout=30)
                
                if response.status_code != 200:
                    raise Exception(f"HTTP {response.status_code}")
                
                data = response.json()
                return self._parse_tencent_history(data, symbol)
            except requests.Timeout:
                if retry < max_retries - 1:
                    logger.warning(f"腾讯财经超时，重试 {retry+1}/{max_retries}")
                    continue
                raise Exception("请求超时")
    
    def _get_history_from_eastmoney(self, symbol: str, days: int) -> List[Dict]:
        """从东方财富获取历史数据"""
        normalized = self.normalize_symbol(symbol)
        market = "1" if normalized.startswith("sh") else "0"
        code = normalized[2:]
        
        url = (
            f"https://push2his.eastmoney.com/api/qt/stock/kline/get"
            f"?secid={market}.{code}&fields1=f1,f2,f3,f4,f5,f6"
            f"&fields2=f51,f52,f53,f54,f55,f56,f57,f58"
            f"&klt=101&fqt=1&lmt={days}"
        )
        
        headers = {
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
            "Referer": "https://quote.eastmoney.com"
        }
        
        response = requests.get(url, headers=headers, timeout=15)
        
        if response.status_code != 200:
            raise Exception(f"HTTP {response.status_code}")
        
        data = response.json()
        return self._parse_eastmoney_history(data)
    
    def _parse_netease_csv(self, csv: str) -> List[Dict]:
        """解析网易历史数据 CSV"""
        prices = []
        lines = csv.strip().split('\n')
        
        for line in lines[1:]:
            if not line:
                continue
            
            try:
                data = line.split(',')
                if len(data) < 8:
                    continue
                
                date_str = data[0]
                close = float(data[3])
                high = float(data[4])
                low = float(data[5])
                open_price = float(data[6])
                volume = int(data[7])
                
                timestamp = int(datetime.strptime(date_str, '%Y-%m-%d').timestamp() * 1000)
                
                prices.append({
                    "timestamp": timestamp,
                    "open": open_price,
                    "high": high,
                    "low": low,
                    "close": close,
                    "volume": volume
                })
                
            except Exception as e:
                logger.error(f"解析网易历史数据失败: {e}")
                continue
        
        prices.reverse()
        
        # 打印数据范围日志
        if prices:
            first_date = datetime.fromtimestamp(prices[0]['timestamp'] / 1000).strftime('%Y-%m-%d')
            last_date = datetime.fromtimestamp(prices[-1]['timestamp'] / 1000).strftime('%Y-%m-%d')
            logger.info(f"📊 网易数据范围（原始）: {first_date} 到 {last_date}，共 {len(prices)} 条")
            logger.info(f"📅 今天日期: {datetime.now().strftime('%Y-%m-%d')}")
        
        return prices
    
    def _parse_tencent_history(self, data: dict, symbol: str) -> List[Dict]:
        """解析腾讯历史数据"""
        prices = []
        
        try:
            normalized = self.normalize_symbol(symbol)
            kline_data = data.get("data", {}).get(normalized, {}).get("qfqday", [])
            
            for item in kline_data:
                # item: [日期, 开盘, 收盘, 最高, 最低, 成交量]
                date_str = item[0]
                timestamp = int(datetime.strptime(date_str, '%Y-%m-%d').timestamp() * 1000)
                
                # 处理成交量，有时是浮点数字符串
                try:
                    volume = int(float(item[5]))
                except (ValueError, TypeError):
                    volume = 0
                
                prices.append({
                    "timestamp": timestamp,
                    "open": float(item[1]),
                    "close": float(item[2]),
                    "high": float(item[3]),
                    "low": float(item[4]),
                    "volume": volume
                })
            
            # 打印数据范围日志
            if prices:
                first_date = datetime.fromtimestamp(prices[0]['timestamp'] / 1000).strftime('%Y-%m-%d')
                last_date = datetime.fromtimestamp(prices[-1]['timestamp'] / 1000).strftime('%Y-%m-%d')
                logger.info(f"📊 腾讯数据范围: {first_date} 到 {last_date}，共 {len(prices)} 条")
                logger.info(f"📅 今天日期: {datetime.now().strftime('%Y-%m-%d')}")
                
        except Exception as e:
            logger.error(f"解析腾讯历史数据失败: {e}")
            import traceback
            traceback.print_exc()
        
        return prices
    
    def _parse_eastmoney_history(self, data: dict) -> List[Dict]:
        """解析东方财富历史数据"""
        prices = []
        
        try:
            # 检查数据结构
            if not data or "data" not in data:
                logger.error("东方财富返回数据结构异常")
                return prices
            
            data_section = data.get("data")
            if not data_section:
                logger.error("东方财富data字段为空")
                return prices
            
            klines = data_section.get("klines", [])
            if not klines:
                logger.warning("东方财富klines为空")
                return prices
            
            for kline in klines:
                # kline: "日期,开盘,收盘,最高,最低,成交量,成交额,振幅,涨跌幅,涨跌额,换手率"
                parts = kline.split(',')
                if len(parts) < 6:
                    continue
                
                date_str = parts[0]
                timestamp = int(datetime.strptime(date_str, '%Y-%m-%d').timestamp() * 1000)
                
                try:
                    volume = int(float(parts[5]))
                except (ValueError, TypeError):
                    volume = 0
                
                prices.append({
                    "timestamp": timestamp,
                    "open": float(parts[1]),
                    "close": float(parts[2]),
                    "high": float(parts[3]),
                    "low": float(parts[4]),
                    "volume": volume
                })
        except Exception as e:
            logger.error(f"解析东方财富历史数据失败: {e}")
            import traceback
            traceback.print_exc()
        
        return prices
